#!/usr/bin/env python

import wsgiref.handlers
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api.labs import taskqueue
from schema import *

import logging

class NodeRanker(webapp.RequestHandler):
  def calculate_previous_rank_from_rank(self, rank):
    computation = rank.computation
    iteration = computation.iteration_count
    dampening_factor = computation.dampening_factor
    if iteration < 1: # No rank has been calc'd; all pages are equally popular.
      return dampening_factor / float(computation.node_count)
    else:
      base_rank = 0
      if len(rank.ranks) > 0: # This node has inlinks
        base_rank += rank.ranks[iteration - 1]
      leakage = computation.leaks[iteration - 1] / computation.node_count
      full_rank = base_rank + leakage
      return dampening_factor * full_rank
  
  def post(self):
    PAGESIZE = 1
    next = None
    computationid = long(self.request.get("computationid"))
    logging.debug("I'll rank nodes for computation %i." % computationid)
    computation = RankComputation.get_by_id(computationid)
    logging.debug("The computation key is %s." % str(computation.key()))
    bookmark = self.request.get("node_key")
    logging.debug("The node key is %s." % bookmark)
    
    nodes = LinkGraphNode.all().order("__key__")
    graph = nodes.filter("computationid =", computationid)
    if bookmark:
      logging.debug("This is a continuation.")
      logging.debug("The last batch left off with %s." % bookmark)
      next_nodes = graph.filter('__key__ >=', db.Key(bookmark))
      batch_of_nodes = next_nodes.fetch(PAGESIZE+1)
    else:
      logging.debug("This is the first batch.")
      batch_of_nodes = graph.fetch(PAGESIZE+1)

      # Calculate leakage
      body = '<form action="/calculate-leakage" method="POST">'
      body += '<input type="hidden" name="computationid" value="'
      body += str(computationid) + '" />'
      body += '<input type="submit" value="Leakage" /></form>'
      self.response.out.write("<html><body>%s</body></html>" % body)
    
    logging.debug("This batch has %i nodes." % len(batch_of_nodes))
    
    if len(batch_of_nodes) == PAGESIZE+1:
      logging.debug("There are enough nodes for another batch.")
      next = batch_of_nodes[-1]
      logging.debug("The next batch starts with node %s." % str(next.key()))
      task = taskqueue.Task(
        url='/rank-nodes',
        params={
          'computationid': str(computationid),
          'node_key': str(next.key()),
        }
      )
      task.add(queue_name='parse')
      batch_of_nodes = batch_of_nodes[:PAGESIZE]
    
      logging.debug("This batch has %i nodes." % len(batch_of_nodes))
    
    logging.debug("Now I'll rank some nodes.")
    for node in batch_of_nodes:
      logging.debug("I'm going to distribute rank from %s." % str(node.key()))

      iteration = computation.iteration_count
      node_count = computation.node_count

      ranks = DocumentRank.all().filter("computationid =", computationid)
      doc_rank = ranks.filter("docid =", db.ByteString(node.docid)).get()
      previous_rank = self.calculate_previous_rank_from_rank(doc_rank)

      outlinks = len(node.links) / 4

      amount = previous_rank / outlinks

      logging.debug("Each link will get %s." % str(amount))

      logging.debug("Now I'll distribute rank to the %i links." % outlinks)
      for i in range(outlinks):
        docid = key_name_from_checksum(node.links[i*4:(i+1)*4])
        task = taskqueue.Task(
          url='/distribute-rank',
          params={
            'computationid': str(computationid),
            'docid': docid,
            'amount': amount,
          }
        )
        task.add(queue_name='parse')


def main():
  logging.getLogger().setLevel(logging.DEBUG)
  wsgiref.handlers.CGIHandler().run(webapp.WSGIApplication([
    ('/rank-nodes', NodeRanker),
  ]))


if __name__ == '__main__':
  main()
